#include "acpi.h"
#include "string.h"
#include "assert.h"
#include "asm.h"
#include "mm.h"

#define EBDA_ADDRESS_POINTER 0x40E

#define RSDP_SIGNATURE "RSD PTR "
// 0x20是空格 0x44是D 0x53是S，0x52是R，所以这个魔数是RSD 的倒转，
// 因为我们机器是小端，会把低位放在前面，所以在内存里是：RSD 
#define ACPI_SIG 0x20445352
#define APIC_SIG 0x43495041

rsdp_t* g_rsdp;
rsdt_t* g_rsdt;
madt_t* g_apic;

// cpu数量
uint32_t g_cpu_number;

local_apic_t g_local_apic[CPU_MAX];

io_apic_t g_io_apic[CPU_MAX];

uint8_t* g_local_apic_addr;

int* find_rsdp(void)
{
    // Check EBDA
    // EBDA是一个计算机科技中的术语，全称是"Extended BIOS Data Area"，
    // 中文翻译为"扩展BIOS数据区"。这是一个存储系统信息的内存区域，
    // 包括一些硬件信息、系统状态等，供BIOS或操作系统在启动时使用。
    ushort ebda_base = *((ushort*) EBDA_ADDRESS_POINTER);
    ebda_base <<= 4;

    for (uint addr = ebda_base; addr < ebda_base + 0x400; addr += 16)
    {
        if (ACPI_SIG == *(int*) addr)
        {
            return (int*) addr;
        }
        
    }
    
    // check main BIOS area
    for (uint addr = 0xE0000; addr < 0x100000; addr += 16)
    {
        if (ACPI_SIG == *(int*) addr)
        {
            return (int*) addr;
        }
        
    }
    return NULL;
}

static uint8_t compute_checksum(void* addr, size_t len)
{
    uint8_t *p = addr;
    uint8_t sum = 0;
    for (size_t i = 0; i < len; i++)
    {
        sum += p[i];
    }
    return sum;
}

void print_rsdp_info(void)
{
    rsdp_t* rsdp = find_rsdp();
    if (NULL == rsdp)
    {
        printk("not found rsdp, exit..\n");
        ENDLESS_LOOP
    }
    
    printk("rsdp address: %08x\n", rsdp);

    if (0 == rsdp->revision)
    {
        if (0 != compute_checksum(rsdp, 20))
        {
            printk("rsdp checksum check fail..\n");
            ENDLESS_LOOP
        }

        printk("rsdp revision: %d->ACPI 1.0\n", rsdp->revision);
    }
    else
    {
        printk("ACPI 2.0 not support..\n");
        ENDLESS_LOOP
    }
    
    char buf[10] = {0};

    memcpy(buf, rsdp->signature, 8);
    printk("rsdp signature: %s\n", buf);

    memset(buf, 0, 10);
    memcpy(buf, rsdp->oem_id, 6);
    printk("rsdp oem_id: %s\n", buf);

    printk("rsdp address: 0x%08x\n", rsdp->rsdt_address);

}

void find_apic()
{
    assert(NULL != g_rsdt);

    if (0 != compute_checksum(g_rsdt, g_rsdt->header.length))
    {
        panic("rsdt data error!\n");
    }
    
    for (int i = 0; i < 8; i++)
    {
        if (0 == g_rsdt->entry[i])
        {
            continue;
        }

        if (APIC_SIG == *(int*) g_rsdt->entry[i])
        {
            g_apic = g_rsdt->entry[i];
            g_local_apic_addr = g_apic->local_controller_address;
            printk("APIC addr: 0x%08x\n", g_apic);
            printk("local_controller_address: 0x%08x\n", g_local_apic_addr);
            break;
        }
        
    }
    
    if (NULL == g_apic)
    {
        panic("not found APIC!\n");
    }
    
}

void get_apic_info(void)
{
    assert(NULL != g_apic);

    if (0 != compute_checksum(g_apic, g_apic->header.length))
    {
        panic("APIC data error!\n");
    }

    uint8_t* addr = &g_apic->table;
    uint8_t* addr_end = (uint8_t*) g_apic + g_apic->header.length;

    while (addr < addr_end)
    {
        if (0 == *addr)
        {
            local_apic_t *apic = addr;

            g_cpu_number++;
            assert(g_cpu_number < CPU_MAX);

            local_apic_t* des = (local_apic_t *) ((uint8_t*)&g_local_apic + apic->apic_id * sizeof(local_apic_t));
            memcpy(des, apic, apic->header.length);

            addr += apic->header.length;
        }
        else if (1 == *addr)
        {
            io_apic_t* apic = addr;

            io_apic_t* des = (io_apic_t*) ((uint8_t*) &g_io_apic + apic->io_apic_id * sizeof(io_apic_t));
            memcpy(des, apic, apic->header.length);

            physics_addr_map_virtual_addr_2m_2(apic->io_apic_address, apic->io_apic_address, 0x9f, 0x1f);
            printk("io apic address: 0x%08x\n", apic->io_apic_address);

            addr += apic->header.length;
        }
        else 
        {
            addr += *(addr + 1);
        }
        
    }
    
    
}

void acpi_init(void)
{
    g_rsdp = find_rsdp();
    if (0 == g_rsdp->revision)
    {
        if (0 != compute_checksum(g_rsdp, 20))  
        {
            printk("rsdp checksum check fail..\n");
            ENDLESS_LOOP
        }
        
        printk("rsdp revision: %d->ACPI 1.0\n", g_rsdp->revision);
        // 为RSDT address做虚拟内存映射
        {
            char* addr = g_rsdp->rsdt_address;
            printk("rsdp address:0x%8x\n", addr);
            g_rsdt = addr;
            physics_map_virtual_addr_2m(addr, addr);
        }
        {
            find_apic();
            physics_addr_map_virtual_addr_2m_2(g_apic->local_controller_address, 
                g_apic->local_controller_address, 0x9f, 0x1f);
        }

        {
            get_apic_info();
        }
    }
    else
    {
        printk("ACPI2.0, not support yet..\n");
        ENDLESS_LOOP
    }
    
    // print_apic_info();
}

void print_rsdt_info(void)
{
    assert(NULL != g_rsdt);
    char buf[10] = {0};

    if (0 != compute_checksum(g_rsdt, g_rsdt->header.length))
    {
        panic("rsdt data error!\n");
    }
    
    printk("=========== start: RSDT INFO ============\n");
    printk("rsdt addr: 0x%08x\n", g_rsdt);
    printk("rsdt length: %d\n", g_rsdt->header.length);
    printk("rsdt entry addr: 0x%08x\n", (char*) g_rsdt + sizeof(acpi_sdt_header_t));

    memcpy(buf, g_rsdt->header.signature, 4);
    printk("rsdt signature: %s\n", buf);

    printk("rsdt revision: %d\n", g_rsdt->header.revision);

    memset(buf, 0, 10);
    memcpy(buf, g_rsdt->header.oem_id, 6);
    printk("rsdt oem_id:%s\n", buf);

    memset(buf, 0, 10);
    memcpy(buf, g_rsdt->header.oem_table_id, 8);
    printk("rsdt oem_table_id: %s\n", buf);

    printk("rsdt oem_revision: 0x%08x\n", g_rsdt->header.oem_revision);
    printk("rsdt creator_id: 0x%08x\n", g_rsdt->header.creator_id);
    printk("rsdt creator_revision: 0x%08x\n", g_rsdt->header.creator_revision);

    printk("rsdt MADT list: \n");

    for (int i = 0; i < 8; i++)
    {
        if (0 == g_rsdt->entry[i])
        {
            continue;
        }
        printk("%d     0x%08x\n", i, g_rsdt->entry[i]);
    }
    
    printk("======== end: RSDT INFO ==========\n");
}

void print_apic_info(void)
{
    assert(NULL != g_apic);
    if (0 != compute_checksum(g_apic, g_apic->header.length))
    {
        panic("APIC data error!\n");
    }

    printk("============ start: APIC INFO =======\n");
    printk("APIC local_controller_address: 0x%08x\n", g_apic->local_controller_address);

    printk("APIC flags: 0x%08x\n", g_apic->flags);

    uint8_t* addr = &g_apic->table;
    uint8_t* addr_end = (uint8_t*) g_apic + g_apic->header.length;
    while (addr < addr_end)
    {
        if (0 == *addr)
        {
            local_apic_t *apic = addr;

            printk("------------\n");
            printk("Local APIC len: %d\n", apic->header.length);
            printk("acpi_processor_id: 0x%02x\n", apic->acpi_processor_id);
            printk("apic_id: 0x%02x\n", apic->apic_id);
            printk("flags: 0x%08x\n", apic->flags);
            printk("------------\n");

            addr += apic->header.length;
        }
        else if (1 == *addr)
        {
            io_apic_t* apic = addr;
            printk("------------\n");
            printk("IO APIC len: %d\n", apic->header.length);
            printk("io_apic_id: 0x%02x\n", apic->io_apic_id);
            printk("io_apic_address: 0x%08x\n", apic->io_apic_address);
            printk("global_system_interrup_base: 0x%08x\n", apic->global_system_interrupt_base);
            printk("------------\n");
            addr += apic->header.length;
        }
        else 
        {
            addr += *(addr + 1);
        }
    }
    printk("============ end: APIC INFO =======\n");
}
